package controllers

import (
	"fmt"
	"ginstudydemo.com/main/models"
	"ginstudydemo.com/main/service"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"golang.org/x/crypto/bcrypt"
	"time"
)

type IAuthController interface {
	Login()
	Logout()
}

type AuthController struct{}

func NewAuthController() *AuthController {
	return &AuthController{}
}

// Login 登录功能
func (u AuthController) Login(c *gin.Context) {

	//使用数据表结构体获取参数
	param := &models.SysUserUsersLoginBody{}
	// 把结构体指针传入BindJSON，这样param的值才能同步修改
	cErr := c.BindJSON(param)

	if !(cErr == nil) {
		service.ReturnErr(c, "5001", cErr)
		c.Abort()
		return
	}

	//TODO: 验证表单信息  1. 用户名邮箱手机号必须有一个有值  2.邮箱格式 手机号格式

	//TODO:  数据库验证手机号是存在（目前先实现手机登录）或 邮箱是存在 然后取出密码 model中实现 或者以后加 service model制作查询 service封装model查询的方法

	modelUserInfo, modelErr := models.GetUserWithRole(*param)
	if !(modelErr == nil) {
		service.ReturnErr(c, "5002", modelErr)
		c.Abort()
		return
	}
	fmt.Println(modelUserInfo)

	//判断输入的明文密码和数据库中的加密密码是否匹配
	/**
	bcrypt.CompareHashAndPassword 是 bcrypt 包中的一个函数，用于比较已加密的哈希密码和未加密的明文密码，返回错误信息来表示密码是否匹配。
	第一个参数是加密过的密码（哈希值）。
	第二个参数是用户输入的明文密码。
	#[]byte(modelUserInfo.Password)
	如果哈希密码和明文密码匹配，则返回 nil，表示验证通过；如果不匹配，则返回一个错误，通常会进一步处理错误来通知用户密码错误。
	modelUserInfo.Password 是数据库中存储的用户的哈希密码。由于 bcrypt.CompareHashAndPassword 期望输入的密码是字节数组（[]byte），所以需要将密码从字符串转换为字节数组。
	假设 modelUserInfo 是从数据库中查询得到的用户信息结构体，它的 Password 字段存储的是已经使用 bcrypt 加密后的哈希密码。
	#[]byte((*param).Password)
	(*param).Password 是用户通过登录表单提交的明文密码。这里 param 是一个指针，指向包含用户输入数据的结构体，其中 Password 是用户输入的未加密密码。
	和 modelUserInfo.Password 一样，我们需要将明文密码转换为字节数组，以便与加密的哈希密码进行比较。
	*/
	bcryptErr := bcrypt.CompareHashAndPassword([]byte(modelUserInfo.Password), []byte((*param).Password))
	if bcryptErr != nil {
		service.ReturnErr(c, "5003", "密码不正确")
		c.Abort()
		return
	}

	// 获取角色按钮权限列表
	modelPerms, modelPermsErr := models.GetSysRoleMenuPerm(modelUserInfo.Roles)
	if !(modelPermsErr == nil) {
		service.ReturnErr(c, "5002", modelPermsErr)
		c.Abort()
		return
	}
	// 将角色按钮权限列表存入Redis哈希表
	service.NewAuthService().SavePermsAsGoHashSetToRedis(modelUserInfo.Roles, *modelPerms)

	// 使用 copier 复制结构体 .UserWithRole  到  SysUserUsersLoginTokenUserInfo
	var tokenUserInfo models.SysUserUsersLoginTokenUserInfo
	cpErr := copier.Copy(&tokenUserInfo, &modelUserInfo)
	if cpErr != nil {
		service.ReturnErr(c, "5003", "密码不正确")
		c.Abort()
		return
	}

	// 生成一个带有自定义声明（claims）的 JWT（JSON Web Token）令牌。
	token, tkErr := service.NewJwtV5().EncryptUserToken(tokenUserInfo)
	if tkErr != nil {
		service.ReturnErr(c, "5003", tkErr)
		c.Abort()
		return
	}

	//返回结果
	service.ReturnSuccess[string](c, "2000", "用户ok", token)
}

// Logout 注销登录
func (u AuthController) Logout(c *gin.Context) {
	token := c.GetHeader("Authorization")
	if token == "" {
		service.ReturnErr(c, "4001", "Authorization token is required")
		return
	}

	// 验证JWT token的有效性
	claims, tokenErr := service.NewJwtV5().DecodeUserToken(token)
	if tokenErr != nil {
		service.ReturnErr(c, "4002", "Invalid token")
		return
	}

	// 将 token 添加到黑名单，黑名单的过期时间与 token 的过期时间一致
	expiration := time.Until(claims.ExpiresAt.Time) // 通过字段名访问
	SvErr := service.NewAuthService().AddTokenToBlacklist(token, expiration)
	if SvErr != nil {
		service.ReturnErr(c, "4003", "Failed to blacklist token")
		return
	}

	service.ReturnSuccess[string](c, "2000", "Logout successful", "")

}
